import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
import WithErrorBoundary from '../WithErrorBoundary'
import { FrameSty } from './style'

interface List {
  x: number,
  y: number,
  text: string,
  fontSize: number,
  width: number,
  speed: number,
}

function Rain() {
  const frameDom = useRef(null)
  const canvasDom = useRef<any>(null)
  const canvasCtx = useRef<any>(null)
  const [width, setWidth] = useState(0)
  const [height, setHeight] = useState(0)
  /** 数字雨总数*/
  const amount = useRef(100);
  /** 已存在的数字雨*/
  const dataList = useRef<List[]>([])
  const animation = useRef<any>(null)


  /** 设置canvas的宽高*/
  const setCanvasSize = useCallback(() => {
    if (frameDom.current === null) {
      return
    }
    let frameInfo = (frameDom.current as any).getBoundingClientRect()
    const screenWidth = frameInfo.width;
    const screenHeight = frameInfo.height;
    setHeight(screenHeight)
    setWidth(screenWidth)
  }, [])

  /** 生成随机数*/
  const createRandomNum = useCallback((min: number, max: number) => {
    return Math.floor(Math.random() * (max - min + 1)) + min;
  }, [])


  /**
  * @description 创建01字符串
  * @returns 01字符串
  */
  function generateRandomString() {
    const characters = '01';
    const len = Math.floor(Math.random() * (60 - 45 + 1)) + 45;
    let randomString = '';

    for (let i = 0; i < len; i++) {
      const randomIndex = Math.floor(Math.random() * characters.length);
      randomString += characters[randomIndex];
    }
    return randomString;
  }

  /** 单条数字雨生成*/
  const drawSeparateLine = useCallback((fontSize: number, width: number, x: number, y: number, text: string) => {
    canvasCtx.current.font = `bold ${fontSize}px Arial`
    let grd = canvasCtx.current.createLinearGradient(x, y, x + width, y);
    grd.addColorStop(0, "aqua");
    grd.addColorStop(1, "transparent");
    canvasCtx.current.fillStyle = grd
    canvasCtx.current.shadowColor = "aqua";  // 设置阴影颜色
    canvasCtx.current.shadowBlur = 20;  // 设置阴影的模糊程度
    canvasCtx.current.fillText(text, x, y)
  }, [])

  /**
  * @description 记录数据
  * @param x 起始坐标 {number} 
  * @param y 起始坐标 {number} 
  * @param text 文字 {string} 
  * @returns
  */
  const recordData = useCallback((x: number, y: number, text: string) => {
    const fontSize = createRandomNum(17, 24)
    const speed = createRandomNum(2, 4)
    let textOne = canvasCtx.current.measureText(text);
    drawSeparateLine(fontSize, textOne.width, x, y, text)

    dataList.current.push({
      x,
      y,
      text,
      fontSize,
      width: textOne.width,
      speed
    })
  }, [])

  /** 画整个页面*/
  const draw = useCallback(() => {
    canvasCtx.current.clearRect(0, 0, width, height)
    canvasCtx.current.fillStyle = 'black';
    canvasCtx.current.fillRect(0, 0, width, height);

    /** 移动*/
    for (let i = 0; i < dataList.current.length; i++) {
      let item = dataList.current[i];
      drawSeparateLine(item.fontSize, item.width, item.x - item.speed, item.y, item.text)
      dataList.current[i] = {
        ...dataList.current[i],
        x: item.x - item.speed
      }
    }

    /**  增加新的*/
    const maxWidth = window.innerWidth * 3;
    const minWidth = window.innerWidth;
    const maxHeight = window.innerHeight;
    const minHeight = 0;
    for (let i = 0; i < amount.current - dataList.current.length; i++) {
      let x = createRandomNum(minWidth, maxWidth)
      let y = createRandomNum(minHeight, maxHeight)
      recordData(x, y, generateRandomString())
    }

    /** 去除旧的*/
    let list: number[] = [];
    for (let i = 0; i < dataList.current.length; i++) {
      if (dataList.current[i].x + dataList.current[i].width <= 0) {
        list.push(i)
      }
    }
    dataList.current = dataList.current.filter((item, ind) => {
      return !list.includes(ind)
    })

    animation.current = requestAnimationFrame(draw);
  }, [width, height])

  useEffect(() => {
    if (canvasDom.current === null || frameDom.current === null) {
      return
    }
    canvasCtx.current = canvasDom.current.getContext('2d');
    const resizeObserver = new ResizeObserver(entries => {
      setCanvasSize()
    });
    resizeObserver.observe(frameDom.current);
    return () => {
      resizeObserver.disconnect()
    }
  }, [])

  useEffect(() => {
    canvasCtx.current.fillStyle = 'black';
    canvasCtx.current.fillRect(0, 0, width, height);

    cancelAnimationFrame(animation.current);
    draw()
  }, [height, width])

  return (
    <>
      <FrameSty ref={frameDom} >
        <canvas ref={canvasDom}
          width={width}
          height={height}
        ></canvas>
      </FrameSty>
    </>
  )
}

export default WithErrorBoundary(Rain)
